home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************
- * *
- * step.c takes care of stepping the data in a window, and contains doBackground, *
- * the main routine to this effect. This and finishCopy should be the only routines *
- * called from the outside, unless some special effect is desired. *
- * *
- *******************************************************/
-
- #include "main.h"
-
- extern WindowPtr frontUserWindow(),
- messyLevel;
- extern dataHandle currentData;
- extern int isBackground;
-
- long lifeSpeed = 260, /* These ought to be computed at startup. */
- copySpeed = 512;
-
- /*******************************************************
- * *
- * Obnoxious code - runs life for a certain number of longwords. It starts with *
- * pointers for the current line, the previous line, and the next line, along with *
- * a number of longwords to compute. It works in the negative direction, and at *
- * the end has a longword left over, which should be stored or passed on the next *
- * call as oldval. It operates via a logic function on 32 pixels at once, resulting *
- * in a total speed of about 4 cycles (68020) per pixel, or 4Mpx/sec. *
- * *
- *******************************************************/
-
- long lifeLines( counts, prev, curr, next, dest, oldval )
- long counts, *prev, *curr, *next, *dest, oldval;
- {
- asm {
- movem.l d3-d7/a2-a4,-(sp)
- movem.l counts,d7/a0-a4
- subq #1,d7
- clr.b d5
- load: move.l -(a0),d0
- move.l -(a2),d1
- move.l -(a1),d6 /* 21 */
- precalc: eor.l d1,d0
- eor.l d6,d1
- or.l d0,d1
- eor.l d6,d0
- eor.l d0,d1
- addx.b d5,d5
- addx.l d6,d6 /* 14 */
- mixed: move.l d0,d2
- not.l d0
- addx.b d5,d5
- addx.l d2,d2
- move.l d2,d4
- addx.b d5,d5
- addx.l d4,d4
- not.l d4
- eor.l d4,d0
- or.l d0,d4
- eor.l d0,d2
- or.l d2,d0
- move.l d1,d3
- not.l d1
- addx.b d5,d5
- addx.l d3,d3
- eor.l d3,d0
- addx.b d5,d5
- addx.l d3,d3
- not.l d3
- eor.l d3,d1
- or.l d1,d3
- eor.l d0,d4
- or.l d4,d0
- or.l d6,d2
- eor.l d1,d4
- and.l d4,d2
- and.l d0,d2
- and.l d3,d2
- addx.b d5,d5 /* 60 */
- lsl.b #3,d5
- fixshift: lsr.l #1,d2
- exg d2,a4
- bcc @store
- bset #31,d2 /* 13 */
- store: move.l d2,-(a3) /* 5 */
- loop: dbf d7,@load /* 6 */ /* == 123 */
- move.l a4,d0
- movem.l (sp)+,d3-d7/a2-a4
- }
- }
-
- /*******************************************************
- * *
- * fixEdges updates the opposite edges from each other. This works out to be *
- * faster than including it in the main life routine, as this way it need not worry *
- * about where the edges are, or use any registers for this. Also, this way *
- * fixEdges can be used with any simple automaton, ie one which has only a one- *
- * pixel neighborhood. *
- * *
- *******************************************************/
-
- fixEdges( theData )
- dataPtr theData;
- {
- char *ldata, *rdata;
- int perline, h, lbit, rbit, which;
-
- which = -theData->offBits.bounds.left;
- lbit = 32 - which;
- which += theData->offBits.bounds.right - 1;
- rbit = 31 - (( which & 15 ) + 1 );
- ldata = theData->offBits.baseAddr;
- rdata = ldata + (( which >> 3 ) & wordMask );
- perline = theData->offBits.rowBytes;
- h = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
- asm {
- movem.l d3-d7,-(sp)
- move h,d7
- move perline,d6
- move lbit,d5
- move rbit,d4
- moveq #0,d3
- move.l d3,d2
- bset d5,d3
- bset d4,d2
- subq #1,d5
- addq #1,d4
- move.l ldata,a1
- move.l rdata,a0
- loop: move.l (a1),d1
- move.l (a0),d0
- btst d5,d1
- beq @skiprnot
- not.l d0
- btst d4,d0
- bne @skiplnot
- not.l d1
- bra @skiplnot
- skiprnot: btst d4,d0
- beq @skiplnot
- not.l d1
- skiplnot: and.l d3,d1
- and.l d2,d0
- eor.l d1,(a1)
- eor.l d0,(a0)
- add d6,a1
- add d6,a0
- dbf d7,@loop
- movem.l (sp)+,d3-d7
- }
- }
-
- /*******************************************************
- * *
- * changeData executes as much of the stepping process on theData as possible *
- * in the given amount of time, and if it finishes one generation before it has used *
- * the time up, it changes the time to the amount that is left over. The cases for *
- * the first and last lines are slightly different, as it must wrap above or below. *
- * *
- *******************************************************/
-
- changeData( theData, time )
- dataPtr theData;
- int *time;
- {
- register int high, wide, state;
- long lines, words, offset;
- long whole, *prev, *curr, *next, *dtop, *dest, *dend, *stop, oldval;
-
- state = theData->partDone;
- wide = theData->offBits.rowBytes / 4;
- high = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
- lines = ( *time * lifeSpeed ) / wide;
- lines = state ? min( state, lines ) : min( high, lines );
- words = lines * wide;
- *time -= words / lifeSpeed;
- dtop = ( long* )theData->bases[ 1 ];
- stop = ( long* )theData->bases[ 0 ];
- if( !state ) {
- fixEdges( theData );
- offset = ( long )high * wide;
- next = stop + wide;
- prev = stop + offset;
- curr = prev + wide;
- dest = ( offset + wide + 1 ) + dtop;
- oldval = lifeLines(( long )wide, prev, curr, next, dest, 0L );
- if( lines > 1 )
- oldval = lifeLines(( lines - 1) * wide, prev - wide, prev, curr, dest - wide, oldval );
- state = high - lines;
- } else {
- offset = ( long )state * wide;
- prev = offset + stop;
- curr = prev + wide;
- next = curr + wide;
- dest = ( offset + wide ) + dtop;
- oldval = *dest++;
- oldval = lifeLines( lines * wide, prev, curr, next, dest, oldval );
- state -= lines;
- }
- if( !state ) {
- dest = dtop + wide + 1;
- curr = stop + wide;
- offset = ( long )high * wide;
- *dtop = lifeLines(( long )wide, curr + offset, curr, curr + wide, dest, oldval );
- state = -1;
- } else *( dtop + ( long )( state + 1 ) * wide ) = oldval;
- theData->partDone = state;
- }
-
- /*******************************************************
- * *
- * StripEdges is used by checkCycles to remove extraneous pixels along the edges *
- * of theData, so that it is comparable with the saved image. *
- * *
- *******************************************************/
-
- stripEdges( theData )
- dataPtr theData;
- {
- char *base;
- long maskl, maskr;
- int width, height;
-
- base = theData->bases[ 0 ];
- width = theData->offBits.rowBytes - 4;
- height = theData->offBits.bounds.bottom - theData->offBits.bounds.top - 1;
- maskl = -1L >> -theData->offBits.bounds.left;
- maskr = -1L << ( width * 8 - theData->offBits.bounds.right + theData->offBits.bounds.left );
- asm {
- move d3,a1
- move.l base,a0
- move.l maskl,d0
- move.l maskr,d1
- move width,d2
- move height,d3
- loop: and.l d0,(a0)
- add d2,a0
- and.l d1,(a0)+
- dbf d3,@loop
- move a1,d3
- }
- }
-
- /*******************************************************
- * *
- * blockCompare is quite simple - it returns whether the data at the two pionters *
- * are the same or not. *
- * *
- *******************************************************/
-
- blockCompare( base0, base1, length )
- char *base0, *base1;
- long length;
- {
- asm {
- move.l base0,a0
- move.l base1,a1
- move.l length,d0
- lsr.l #2,d0
- move d0,d1
- swap d0
- ori #4,CCR /* Z flag */
- bra @entry
- loop: cmpm.l (a0)+,(a1)+
- entry: dbne d1,@loop
- dbne d0,@loop
- seq d0
- ext.w d0
- }
- }
-
- /*******************************************************
- * *
- * checkCycle is called once every cycleLength generations to check for cycles. *
- * If a cycle exists, the data in this case is restarted with a random pattern. In *
- * future, more interesting things may be implimented, such as finding the point *
- * at which the data began to cycle (requiring another bitmap) or finding the actual *
- * (minimal) length of the cycle, by factoring the cycle length. *
- * *
- *******************************************************/
-
- checkCycle( theData )
- dataPtr theData;
- {
- int result;
- char *source, *dest;
-
- stripEdges( theData );
- source = theData->bases[ 1 ];
- dest = *( theData->cycleBase );
- result = blockCompare( source, dest, theData->planeSize );
- if( result ) {
- randomize( &( theData->offBits ), source );
- theData->generation = 0;
- }
- BlockMove( source, dest, theData->planeSize );
- theData->cycleGeneration = theData->generation;
- return( result );
- }
-
- /*******************************************************
- * *
- * copyData executes as much of the copying needed to bring a new generation to *
- * the screen as it can in the given time, assuming that gray is a region containing *
- * whatever gray is painted on the screen at the moment it is called. finishCopy *
- * is called when a window is forced to stop, and is already partialy copied. (if *
- * no data has been copied, the new data is simply thrown away). *
- * *
- *******************************************************/
-
- finishCopy( theData )
- dataPtr theData;
- {
- int temp = 0;
-
- copyData( theData, &temp, NULL );
- }
-
- copyData( theData, time, gray )
- dataPtr theData;
- int *time;
- RgnHandle gray;
- {
- Rect bounds;
- RgnHandle maskRgn;
- GrafPtr savePort;
- WindowPeek theWindow;
- int high, wide, lines, yetToDo, toDraw = 0;
-
- GetPort( &savePort );
- SetPort( theWindow = ( WindowPeek )( theData->parent ));
- if( theData->partDone == -1 ) {
- swapPlane( theData );
- madeChange( theData );
- ++( theData->generation );
- toDraw = bitsBadGen;
- }
- if( !EmptyRgn( theWindow->updateRgn )) {
- theData->partDone = 0;
- displayData( theData, bitsBadData | toDraw, gray );
- *time = 0;
- } else {
- if( toDraw ) displayData( theData, toDraw, gray );
- high = theData->dataRect.bottom - theData->dataRect.top;
- yetToDo = high + theData->partDone + 1;
-
- if( *time ) {
- wide = theData->offBits.rowBytes / 4;
- lines = ( *time * copySpeed ) / wide;
- lines = min( lines, yetToDo );
- *time -= (( long )lines * wide ) / copySpeed;
- } else lines = yetToDo;
-
- bounds = theData->dataRect;
- bounds.top += high - yetToDo;
- bounds.bottom = bounds.top + lines;
- if( theData->isSelect ) {
- maskRgn = NewRgn();
- RectRgn( maskRgn, &( theData->dataRect ));
- DiffRgn( maskRgn, theData->selRgn, maskRgn );
- } else maskRgn = NULL;
- copyDisplay( &( theData->offBits ), &bounds, maskRgn, gray );
- if( maskRgn ) DisposeRgn( maskRgn );
- yetToDo -= lines;
- theData->partDone = yetToDo ? -( high - yetToDo + 1 ) : 0;
- }
- if( theData->state == dataStepping && theData->partDone == 0 ) {
- theData->state = dataStopped;
- --isBackground;
- }
- SetPort( savePort );
- }
-
- /*******************************************************
- * *
- * StepData is called to while away a certain amount of time on one data element. *
- * If stops after it has finished copying new data to the screen, if it gets that far. *
- * It takes care of all parts of generating the next generation, including the cycle *
- * detection and 'auto restart'. It is called with a dataHandle - all routines below *
- * it are called with pointers to the locked handle. *
- * *
- *******************************************************/
-
- stepData( theDataHand, time, gray )
- dataHandle theDataHand;
- RgnHandle gray;
- int *time;
- {
- dataPtr theData;
-
- HLock( theDataHand );
- theData = *theDataHand;
- if( theData->partDone >= 0 ) {
- changeData( theData, time );
- if( theData->partDone == -1 && theData->cycleFlags != cycleNone &&
- theData->generation - theData->cycleGeneration == theData->cycleLength )
- checkCycle( theData );
- }
- if( theData->partDone < 0 && *time )
- copyData( theData, time, gray );
- HUnlock( theDataHand );
- }
-
- /*******************************************************
- * *
- * DrawAnyAnts takes care of any crawly-ants (active selections) on-screen at *
- * the time it is called, which is once per call to doBackground. It perhaps should *
- * be called every few ticks, but some prettyness in the crawly-ants is given up *
- * to gain efficiency in the other background jobs. This must be changed if it is *
- * found that doBackground is called with extremely long waits. *
- * *
- *******************************************************/
-
- drawAnyAnts() {
- dataHandle theDataHand;
- WindowPtr theWindow;
- dataPtr theData;
- int hState, change;
-
- theWindow = frontUserWindow();
- if( theWindow ) {
- theDataHand = ( dataHandle )GetWRefCon( theWindow );
- hState = HGetState( theDataHand );
- HLock( theDataHand );
- theData = *theDataHand;
- if( theData->isSelect && theData->selActive && ( change = updateAnts( theData )))
- drawAnts( theData, change );
- HSetState( theDataHand, hState );
- }
- }
-
- /*******************************************************
- * *
- * DoBackground is called as often as possible with the estimated amount of time *
- * available, a flag saying what the contents of theOutline consist of, and a region *
- * describing an area of the screen which is painted with gray. GrayPart may be *
- * noGray, in which case theOutline should be NULL, someGray, in which case the *
- * global WindowPeek messyLevel is checked against each window, or allGray, in *
- * which case it is assumed that all windows may be trashed. *
- * DoBackground also calls finishChannels to take care of sounds which may *
- * be queued by the sound patches. This perhaps should only be done in the fore- *
- * ground, but that would seem counter-intuitive to the user. *
- * *
- *******************************************************/
-
- doBackground( time, grayPart, theOutline )
- int time, grayPart;
- RgnHandle theOutline;
- {
- dataHandle nextDataHand, startData;
- dataPtr theData;
- int hState, doOutline;
-
- if( !isBackground ) return;
- finishChannels();
- drawAnyAnts();
- startData = currentData;
- if( currentData )
- do {
- hState = HGetState( currentData );
- HLock( currentData );
- theData = *currentData;
- if( theData->state != dataStopped ) {
- doOutline = grayPart == allGray || ( grayPart == someGray && !cleanWindow( theData->parent ));
- stepData( currentData, &time, doOutline ? theOutline : NULL );
- }
- nextDataHand = theData->next;
- HSetState( currentData, hState );
- currentData = nextDataHand;
- } while( time && currentData != startData );
- }